// ============================================================================
// FileName: SIPSwitchServices.cs
//
// Description:
// Web services for the SIP Switch.
//
// Author(s):
// Aaron Clauson
//
// History:
// 02 Nov 2006	Aaron Clauson	Created.
// 20 Oct 2007  Aaron Clauson   Added the use dial plan for incoming option.
// ============================================================================

using System;
using System.Collections;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.ServiceModel;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.Xml;
using BlueFace.Sys;
using BlueFace.Sys.Net;
using BlueFace.Sys.WebServices;
using BlueFace.VoIP.Net.SIP;
using BlueFace.VoIP.App.SIP;
using BlueFace.VoIP.SIPServer;
using BlueFace.VoIP.SIPServerCores.StatefulProxy;
using log4net;
using SIPSwitch.Web.SIPProvisioningServiceProxy;

namespace SIPSwitch.Web
{
	[WebService(Namespace="http://www.mysipswitch.com")]
	public class SIPSwitchServices : System.Web.Services.WebService
	{	
		private const int AUTHENTICATION_SESSION_TIMEOUT = 30;		// Sessions are valid for 30 minutes of inactivity.
        private const int CLIENT_LOGMESSAGE_LIMIT = 20;             // Number of SIP Switch log messages to display on the client.
        private const int MAX_CLIENTRESPONSE_LENGTH = 2048;          // FireFox bacons out if the SOAP repsonse is too large.
        private const string UNKNOWN_ERROR_MESSAGE = "Sorry an unknown error has occurred processing your request. Please try reloading the page and if the error persists post a message on http://www.mysipswitch.com/forum/index.php.";

        protected TimeSpan m_sessionDuration = new TimeSpan(0, AUTHENTICATION_SESSION_TIMEOUT, 0);	// The default duration that authenticated sessions will be valid for. 
        private static int m_defaultRegExpire = UserRegistration.DEFAULT_REGISTRATION_EXPIRY;
        private static int m_minRegExpire = UserRegistration.MINIMUM_ALLOWED_EXPIRY;

		private static ILog logger = AppState.GetLogger("sipswitch");

        private static string m_switchDBConnStr = ConfigurationManager.AppSettings["SIPSwitchDBConnStr"];
        private static string m_switchStorageTypeStr = ConfigurationManager.AppSettings["SIPSwitchDBType"];
        private static string m_sipSwitchRealm = ConfigurationManager.AppSettings["SIPSwitchRealm"];
        private static string m_sipswitchServersPattern = ConfigurationManager.AppSettings["SIPSwitchServerPattern"];               // Regex pattern identifying the SIPSwitch server that a new provider account will not be accepted for.
        private static string m_disabledRegServersPattern = ConfigurationManager.AppSettings["SIPSwitchDisabledRegistrarServers"];  // List of servers that registrations are being administratively disabled to.
        private static string m_adminUsersPattern = ConfigurationManager.AppSettings["AdminUsersPattern"];                          // Users that will be able to override disbaled registration patterns.
        private string m_binDirectory = ConfigurationManager.AppSettings["BinDirectory"];
        private static string m_sipProxyServiceContractName = ConfigurationManager.AppSettings["SIPProxyWebServiceContractName"];
        private static string m_sipProxyWebServiceURL = ConfigurationManager.AppSettings["SIPProxyWebServiceURL"];
        private static string m_regAgentServiceContractName = ConfigurationManager.AppSettings["RegAgentWebServiceContractName"];
        private static string m_regAgentWebServiceURL = ConfigurationManager.AppSettings["RegAgentWebServiceURL"];
                
       // private SIPProxyWebServiceClient m_sipProxyServiceProxy;
        //private SIPProxyWebServiceClient m_regAgentServiceProxy;
        private SIPManagerServiceProxy m_regAgentServiceProxy;
        private SIPProvisioningWebServiceClient m_regAgentProvisioningWebService;
        private StorageTypes m_sipSwitchStorageType;
        private StorageLayer m_sipSwitchStorageLayer = null;
        private SIPAssetPersistor<SIPProvider> m_sipProviderPersistor;
        private SIPAssetPersistor<DialPlan> m_dialPlanPersistor;

        private string m_rubyDialPlanId = SIPDialPlanEngine.RUBYDIALPLAN_ID;  // The string which indicated whether a dial plan is Ruby or not.

		public SIPSwitchServices()
		{
            try
            {
                m_sipSwitchStorageType = StorageTypesConverter.GetStorageType(m_switchStorageTypeStr);
                m_sipSwitchStorageLayer = new StorageLayer(m_sipSwitchStorageType, m_switchDBConnStr);
                //m_sipProxyServiceProxy = new SIPProxyWebServiceClient(m_sipProxyServiceContractName, m_sipProxyWebServiceURL);
                //m_regAgentServiceProxy = new SIPProxyWebServiceClient(m_regAgentServiceContractName, m_regAgentWebServiceURL);
                BasicHttpBinding binding = new BasicHttpBinding();
                EndpointAddress address = new EndpointAddress("http://localhost:8080/manager");
                m_regAgentServiceProxy = new SIPManagerServiceProxy(binding, address);

                BasicHttpBinding provisioningBinding = new BasicHttpBinding();
                EndpointAddress provisioningAddress = new EndpointAddress("http://localhost:8080/provisioning");
                m_regAgentProvisioningWebService = new SIPProvisioningWebServiceClient(provisioningBinding, provisioningAddress);
                //m_regAgentServiceProxy.Url = "http://localhost:8080/manager";

                //m_regAgentProvisioningWebService = new SIPProvisioningWebService();
                //m_regAgentProvisioningWebService.Url = "http://localhost:8080/provisioning";

                m_sipProviderPersistor = SIPAssetPersistorFactory.CreateSIPProviderPersistor(m_sipSwitchStorageType, m_switchDBConnStr);
                m_dialPlanPersistor = SIPAssetPersistorFactory.CreateDialPlanPersistor(m_sipSwitchStorageType, m_switchDBConnStr);
            }
            catch (Exception excp)
            {
                logger.Error("Exception SIPSwitchServices (ctor). " + excp.Message);
                throw excp;
            }
		}
		
		[WebMethod]
		public bool IsAlive()
		{
			try
			{
                logger.Debug("SIPSwitchServices IsAlive called.");
				return true;
			}
			catch(Exception excp)
			{
				logger.Error("Exception IsAlive. " + excp.Message);
				throw excp;
			}
		}

		[WebMethod]
		public string Echo(string message)
		{
            logger.Debug("SIPSwitchServices Echo called with message of: " + message);
			return message;
		}

        [WebMethod]
        public string GetAuthId(string username, string password)
        {
            try
            {
                // Username is case insensitive and password is case sensitive.
                string selectSQL = "select count(*) from customers where upper(username) = '" + username.Replace("'", "''").ToUpper() + "' and password = '" + password.Replace("'", "''") + "' and suspended <> '1'";
                bool exists = Convert.ToInt32(m_sipSwitchStorageLayer.ExecuteScalar(selectSQL)) == 1;

                if (exists)
                {
                    Guid authId = Guid.NewGuid();
                    logger.Debug("Creating new authenticated session for " + username + ", with id, " + authId + ".");
                    m_sipSwitchStorageLayer.ExecuteNonQuery("insert into AuthenticatedSessions (sessionid, username, ipaddress) values ('" + authId + "', '" + username.Replace("'", "''") + "', '" + this.Context.Request.UserHostAddress + "')");
                    return authId.ToString();
                }
                else
                {
                    logger.Warn("GetAuthId failed for username=" + username + " and password=" + password + ".");
                }
            }
            catch (Exception excp)
            {
                logger.Error("Exception GetAuthId. " + excp.Message);
                throw excp;
            }
        }
		
		[WebMethod]
		public string GetDialPlanExt(string authId)
		{
			try
			{
				logger.Debug("GetDialPlan request for authid=" + authId + ".");
				
				string username = GetSessionUsername(authId);

				if(username != null)
				{
					logger.Debug("Username " + username + " found for authid.");

                    DialPlan dialPlan = m_dialPlanPersistor.Get(username, null);

                    byte[] dialplanArray = Encoding.ASCII.GetBytes(dialPlan.DialPlanScript);
					return Convert.ToBase64String(dialplanArray, 0, dialplanArray.Length);
				}
				else
				{
					logger.Debug("No username found for authid.");
					
					return null;
				}
			}
			catch(Exception excp)
			{
				logger.Error("Exception GetDialPlanExt. " + excp.Message);
				return null;
			}
		}

        [WebMethod]
		public string UpdateDialPlanExt(string authId, string dialPlanExt)
		{
			try
			{
				logger.Debug("UpdateDialPlan request for authid=" + authId + ".");
				
				string username = GetSessionUsername(authId);

				if(username != null)
				{
					logger.Debug("Username " + username + " found for authid.");

					byte[] dialPlanArray = Convert.FromBase64String(dialPlanExt);
					string updatedDialPlan = Encoding.ASCII.GetString(dialPlanArray);

					logger.Debug("Updated dialplan ext=" + updatedDialPlan);

                    if (updatedDialPlan != null && updatedDialPlan.StartsWith(m_rubyDialPlanId))
                    {
                        // Check for illegal Ruby commands.
                        if (Regex.Match(updatedDialPlan, "(require|include|::|system|`)").Success)
                        {
                            return "Ruby dial plans cannot contain \"require \", \"include\", \"::\", \"`\" or \"system\".";
                        }
                    }

					try
					{
						// Make sure the dial plan can compile.
						//SIPDialPlan dialPlan = new SIPDialPlan(null, null, null, updatedDialPlan, null, null);
                        DialPlan dialPlan = m_dialPlanPersistor.Get(username, null);
                        dialPlan.DialPlanScript = updatedDialPlan;
                        m_dialPlanPersistor.Update(dialPlan);

						return "Dial plan extensions successfully updated " + DateTime.Now.ToString("dd MMM yyyy HH:mm:ss") + ".";
					}
					catch(Exception compileExcp)
					{
						logger.Error("Exception building SIP dial plan. " + compileExcp.Message);
						return "Error: " + compileExcp.Message;
					}
				}
				else
				{
					logger.Debug("No username found for authid.");
					
					return "Session has timed out, please take a copy of your dialplan and re-login.";
				}
			}
			catch(Exception excp)
			{
				logger.Error("Exception UpdateDialPlanExt. " + excp.Message);
				return "Error: " + excp.Message;
			}
		}

        [WebMethod]
        public string GetSIPProviderIds(string authId)
        {
            XMLServiceResultStruct opResult = new XMLServiceResultStruct(true, UNKNOWN_ERROR_MESSAGE, null);
            
            try
            {
                logger.Debug("GetSIPProviders request for authid=" + authId + ".");

                string username = GetSessionUsername(authId);

                if (username != null)
                {
                    logger.Debug("Username " + username + " found for authid.");

                    SIPAssetPersistor<SIPProvider> providerPersistor = SIPAssetPersistorFactory.CreateSIPProviderPersistor(m_sipSwitchStorageType, m_switchDBConnStr);
                    List<SIPProvider> providers = providerPersistor.Get(username);

                    opResult.ResultXML = "<providerids>";
                    int count = 0;
                    foreach (SIPProvider provider in providers)
                    {
                        opResult.ResultXML += "<provider id='" + provider.ProviderId + "' name='" + provider.ProviderName + "' />";
                        count++;
                    }
                    opResult.ResultXML += "</providerids>";

                    opResult.Error = false;
                    opResult.Message = count + " SIP Providers found for " + username + ".";
                }
                else
                {
                    logger.Debug("No username found for authid.");
                    opResult.Message = "Session has timed out, please relogin.";
                }
            }
            catch (Exception excp)
            {
                logger.Error("Exception GetSIPProviders. " + excp.Message);
                opResult.Message = "Exception processing request. " + SafeXML.MakeSafeXML(excp.Message);
            }

            //logger.Debug(opResult.ToStringWithRoot());
            return opResult.ToStringWithRoot();
        }

        [WebMethod]
        public string GetSIPProvider(string authId, string providerId)
        {
            XMLServiceResultStruct opResult = new XMLServiceResultStruct(true, UNKNOWN_ERROR_MESSAGE, null);

            try
            {
                logger.Debug("GetSIPProvider request for authid=" + authId + ".");

                string username = GetSessionUsername(authId);

                if (username != null)
                {
                    logger.Debug("Username " + username + " found for authid.");

                    SIPAssetPersistor<SIPProvider> providerPersistor = SIPAssetPersistorFactory.CreateSIPProviderPersistor(m_sipSwitchStorageType, m_switchDBConnStr);
                    SIPProvider provider = providerPersistor.Get(new Guid(providerId));

                    if (provider.Owner == username)
                    {
                        opResult.ResultXML =
                            "<providers>" +
                            provider.ToXML() +
                            "</providers>";

                        opResult.Error = false;
                        opResult.Message = "SIP Provider details for " + provider.ProviderName + " retrieved.";
                    }
                    else
                    {
                        opResult.Message = "An error has occured retrieving the SIP Provider details, owner mismatch.";
                    }
                }
                else
                {
                    logger.Debug("No username found for authid.");
                    opResult.Message = "Session has timed out, please relogin.";
                }
            }
            catch (Exception excp)
            {
                logger.Error("Exception GetSIPProviders. " + excp.Message);
                opResult.Message = "Exception processing request. " + SafeXML.MakeSafeXML(excp.Message);
            }

            //logger.Debug(opResult.ToStringWithRoot());
            return opResult.ToStringWithRoot();
        }

        [WebMethod]
        public string AddOrUpdateSIPProvider(
            string authId,
            string providerId,
            string providerName,
            string providerUsername,
            string providerPassword,
            string providerServer,
            string providerOutboundProxy,
            string providerFrom,
            string providerCustom,
            string registerContact,
            int registerExpiry,
            string registerServer,
            string registerRealm,
            string registerAuthUsername,
            bool registerEnabled)
        {
            XMLServiceResultStruct opResult = new XMLServiceResultStruct(true, UNKNOWN_ERROR_MESSAGE, null);

            try
            {
                logger.Debug("AddOrUpdateSIPProvider request for authid=" + authId + ", providerid=" + providerId + ".");

                Regex sipSwitchPattern = new Regex(m_sipswitchServersPattern, RegexOptions.IgnoreCase);
                Regex disabledRegistrarsPattern = new Regex(m_disabledRegServersPattern, RegexOptions.IgnoreCase);
                Regex adminUsersPattern = new Regex(m_adminUsersPattern, RegexOptions.IgnoreCase);

                string username = GetSessionUsername(authId);

                if (username != null)
                {
                    logger.Debug("Username " + username + " found for authid.");

                    if (providerName == null || providerName.Trim().Length == 0)
                    {
                        opResult.Message = "The SIP Provider Name must be specified.";
                    }
                    else if (providerUsername == null || providerUsername.Trim().Length == 0)
                    {
                        opResult.Message = "The SIP Provider Username must be specified.";
                    }
                    /*else if (providerPassword == null || providerPassword.Trim().Length == 0)
                    {
                        opResult.Message = "The SIP Provider Password must be specified.";
                    }*/
                    else if (providerServer == null || providerServer.Trim().Length == 0)
                    {
                        opResult.Message = "The SIP Provider Server must be specified.";
                    }
                    else if (m_sipswitchServersPattern != null && m_sipswitchServersPattern.Trim().Length > 0 && sipSwitchPattern.Match(providerServer).Success)
                    {
                        opResult.Message = "A SIP Provider entry cannot be created that points at the SIPSwitch.";
                    }
                    else if (registerEnabled && (registerContact == null || registerContact.Trim().Length == 0))
                    {
                        opResult.Message = "The Register Contact must be specified if you wish to register with this provider.";
                    }
                    //else if (providerSocket != null && providerSocket.Trim().Length > 0 && IPSocket.GetIPEndPoint(providerSocket.Trim()) == null)
                    //{
                    //    opResult.Message = "The SIP Provider Socket was not recognised as a valid socket. Please make sure it is in the format 127.0.0.1:5060.";
                    //}
                    else
                    {
                        // Make sure the contact URI is valid.
                        SIPValidationError uriParseError = SIPValidationError.None;
                        if (registerContact != null)
                        {
                            registerContact = (!registerContact.StartsWith("sip:")) ? "sip:" + registerContact : registerContact;
                            SIPUserField contactUserField = SIPUserField.ParseSIPUserField(registerContact, out uriParseError);
                        }

                        if (uriParseError != SIPValidationError.None)
                        {
                            opResult.Message = "The contact address was invalid, " + uriParseError.ToString() + ".";
                        }
                        else
                        {                           
                            if (registerExpiry < m_minRegExpire || registerExpiry > m_defaultRegExpire)
                            {
                                logger.Warn("Requested expiry value of " + registerExpiry + " outside permitted limits, default value being used.");
                                registerExpiry = m_defaultRegExpire;
                            }

                            bool registerAdminEnabled = !disabledRegistrarsPattern.Match(providerServer).Success || adminUsersPattern.Match(username).Success;
                            if (registerAdminEnabled && registerServer != null && registerServer.Trim().Length > 0)
                            {
                                registerAdminEnabled = !disabledRegistrarsPattern.Match(registerServer).Success;
                            }

                            SIPAssetPersistor<SIPProvider> providerPersistor = SIPAssetPersistorFactory.CreateSIPProviderPersistor(m_sipSwitchStorageType, m_switchDBConnStr);
                            SIPProvider provider = null;

                            providerName = providerName.Trim();
                            providerUsername = providerUsername.Trim();
                            providerPassword = providerPassword.Trim();
                            providerServer = providerServer.Trim();
                            providerOutboundProxy = (providerOutboundProxy != null && providerOutboundProxy.Trim().Length > 0) ? providerOutboundProxy.Trim() : null;
                            providerFrom = (providerFrom != null && providerFrom.Trim().Length > 0) ? providerFrom.Trim() : null;
                            providerCustom = (providerCustom != null && providerCustom.Trim().Length > 0) ? providerCustom.Trim() : null;
                            registerContact = (registerContact != null && registerContact.Trim().Length > 0) ? registerContact.Trim() : null;
                            registerServer = (registerServer != null && registerServer.Trim().Length > 0) ? registerServer.Trim() : null;
                            registerRealm = (registerRealm != null && registerRealm.Trim().Length > 0) ? registerRealm.Trim() : null;
                            registerAuthUsername = (registerAuthUsername != null && registerAuthUsername.Trim().Length > 0) ? registerAuthUsername.Trim() : null;

                            if (providerId != null && providerId.Trim().Length > 0)
                            {                               
                                provider = providerPersistor.Get(new Guid(providerId));
                                bool wasRegisterEnabled = provider.RegisterEnabled;

                                provider.ProviderName = providerName.Trim();
                                provider.ProviderUsername = providerUsername.Trim();
                                provider.ProviderPassword = providerPassword.Trim();
                                provider.ProviderServer = providerServer.Trim();
                                provider.ProviderOutboundProxy = providerOutboundProxy;
                                provider.ProviderFrom = providerFrom;
                                provider.CustomHeaders = providerCustom;
                                provider.RegisterContact = registerContact;
                                provider.RegisterExpiry = registerExpiry;
                                provider.RegisterServer = registerServer;
                                provider.RegisterRealm = registerRealm;
                                provider.RegisterAuthUsername = registerAuthUsername;
                                provider.RegisterEnabled = registerEnabled;
                                provider.RegisterAdminEnabled = registerAdminEnabled;

                                if (provider.RegisterEnabled)
                                {
                                    // Clear any registration disabled error message.
                                    provider.RegisterDisabledReason = null;
                                }

                                opResult.Message = "SIP Provider " + providerName + " successfully updated.";
                                providerPersistor.Update(provider);

                                // Let the registration agent know there has been a change.
                                m_regAgentProvisioningWebService.SIPProviderUpdated(provider.ProviderId);
                            }
                            else
                            {
                                provider = new SIPProvider(
                                    username,
                                    providerName,
                                    providerUsername,
                                    providerPassword,
                                    providerServer,
                                    providerOutboundProxy,
                                    providerFrom,
                                    providerCustom,
                                    registerContact,
                                    registerExpiry,
                                    registerServer,
                                    registerAuthUsername,
                                    registerRealm,
                                    registerEnabled,
                                    registerAdminEnabled);

                                opResult.Message = "SIP Provider " + providerName + " successfully added.";
                                providerPersistor.Update(provider);

                                // Let the registration agent know there has been an addition.
                                m_regAgentProvisioningWebService.SIPProviderAdded(provider.ProviderId);
                            }

                            // Mark dial plan as modified.
                            string updateSQL = "update dialplans set updated = '1' where owner = '" + username.Replace("'", "''") + "'";
                            m_sipSwitchStorageLayer.ExecuteScalar(updateSQL);

                            opResult.Error = false;
                            opResult.ResultXML = "<providers>" + provider.ToXML() + "</providers>";
                        }
                    }
                }
                else
                {
                    logger.Debug("No username found for authid.");
                    opResult.Message = "Session has timed out, please relogin.";
                }
            }
            catch (Exception excp)
            {
                logger.Error("Exception AddSIPProvider. " + excp.Message);
                opResult.Message = "Exception processing request. " + excp.Message;
            }

            return opResult.ToStringWithRoot();
        }

        [WebMethod]
        public string DeleteSIPProvider(string authId, string providerId)
        {
            XMLServiceResultStruct opResult = new XMLServiceResultStruct(true, UNKNOWN_ERROR_MESSAGE, null);

            try
            {
                logger.Debug("DeleteSIPProvider request for authid=" + authId + ".");

                string username = GetSessionUsername(authId);

                if (username != null)
                {
                    logger.Debug("Username " + username + " found for authid.");

                    SIPAssetPersistor<SIPProvider> providerPersistor = SIPAssetPersistorFactory.CreateSIPProviderPersistor(m_sipSwitchStorageType, m_switchDBConnStr);
                    SIPProvider provider = providerPersistor.Get(new Guid(providerId));

                    if (provider.Owner == username)
                    {
                        providerPersistor.Delete(provider);

                        // Let the registration agent know there has been a deletion.
                        m_regAgentProvisioningWebService.SIPProviderDeleted(provider.ProviderId);

                        // Mark dial plan as modified.
                        string updateSQL = "update dialplans set updated = '1' where username = '" + username.Replace("'", "''") + "'";
                        m_sipSwitchStorageLayer.ExecuteScalar(updateSQL);

                        opResult.Error = false;
                        opResult.Message = "SIP Provider " + provider.ProviderName + " successfully deleted.";
                        opResult.ResultXML = "<providers><provider id='" + provider.ProviderId + "' /></providers>";
                    }
                    else
                    {
                        opResult.Message = "An error has occured deleting the SIP Provider details, owner mismatch.";
                    }
                }
                else
                {
                    logger.Debug("No username found for authid.");
                    opResult.Message = "Session has timed out, please relogin.";
                }
            }
            catch (Exception excp)
            {
                logger.Error("Exception DeleteSIPProvider. " + excp.Message);
                opResult.Message = "Exception processing request. " + SafeXML.MakeSafeXML(excp.Message);
            }

            //logger.Debug(opResult.ToStringWithRoot());
            return opResult.ToStringWithRoot();
        }

        /// <summary>
        /// Gets a list of the third party registration entries that have been configured for this user in the database.
        /// </summary>
        [WebMethod]
        public string GetRegistrations(string authId)
        {
            XMLServiceResultStruct opResult = new XMLServiceResultStruct(true, UNKNOWN_ERROR_MESSAGE, null);

            try
            {
                //logger.Debug("GetRegistrations request for authid=" + authId + ".");

                string username = GetSessionUsername(authId);

                if (username != null)
                {
                    //logger.Debug("Username " + username + " found for authid.");

                    opResult = GetUserRegistrationsInternal(username);
                }
                else
                {
                    logger.Debug("No username found for authid.");
                    opResult.Message = "Session has timed out, please relogin.";
                }
            }
            catch (Exception excp)
            {
                logger.Error("Exception GetRegistrations. " + excp.Message);
                opResult.Message = "Exception processing request. " + excp.Message;
            }

            return opResult.ToStringWithRoot();
        }

		#region Pass through services to the SIP Proxy Server.

        [WebMethod]
        public bool IsProxyAlive()
        {
            try
            {
                logger.Debug("IsProxyAlive called.");

                return m_regAgentServiceProxy.IsAlive();
            }
            catch (Exception excp)
            {
                logger.Error("Exception IsProxyAlive. " + excp.Message);
                return false;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="authId"></param>
        /// <returns></returns>
        [WebMethod]
        public string Get3rdPartyRegistrationDetails(string authId, string registrationId)
        {
            XMLServiceResultStruct opResult = new XMLServiceResultStruct(true, UNKNOWN_ERROR_MESSAGE, null);

            try
            {
                //logger.Debug("GetRegistrationsFromProxy request for authid=" + authId + ".");

                string username = GetSessionUsername(authId);

                if (username != null)
                {
                    //logger.Debug("Username " + username + " found for authid.");

                    UserRegistration[] ownerRegistrations = m_regAgentServiceProxy.GetRegistrationAgentRegistrations(username);
                    //List<SIPProvider> ownerRegistrations = m_sipProviderPersistor.Get(username);
                    if (ownerRegistrations != null && ownerRegistrations.Length > 0)
                    {
                        foreach (UserRegistration ownerReg in ownerRegistrations)
                        {
                            if (ownerReg.RegistrationId.ToString() == registrationId)
                            {
                                opResult = new XMLServiceResultStruct(false, "Registration " + registrationId + " found", ownerReg.ToXML());
                                break;
                            }
                        }
                    }
                    else
                    {
                        opResult.Message = "Requested registration could not be found.";
                    }
                }
                else
                {
                    logger.Warn("No username found for authid.");
                    opResult.Message = "Session has timed out, please relogin.";
                }
            }
            catch (Exception excp)
            {
                logger.Error("Exception Get3rdPartyRegistrationDetails. " + excp.Message);
                opResult.Message = "Exception processing request. " + excp.Message;
            }

            return opResult.ToStringWithRoot();
        }

        /// <summary>
        /// Gets a list of the id's for the user's third party registrations (note this mehod only returns the id's not the details).
        /// </summary>
        /// <param name="authId"></param>
        /// <returns></returns>
        [WebMethod]
        public string Get3rdPartyRegistrationIds(string authId)
        {
            XMLServiceResultStruct opResult = new XMLServiceResultStruct(true, UNKNOWN_ERROR_MESSAGE, null);

            try
            {
                //logger.Debug("GetRegistrationsFromProxy request for authid=" + authId + ".");

                string username = GetSessionUsername(authId);

                if (username != null)
                {
                    //logger.Debug("Username " + username + " found for authid.");

                    //List<UserRegistration> ownerRegistrations = m_regAgentServiceProxy.GetCurrentSIPRegistrations(username);
                    UserRegistration[] ownerRegistrations = m_regAgentServiceProxy.GetRegistrationAgentRegistrations(username);
                    if (ownerRegistrations != null && ownerRegistrations.Length > 0)
                    {
                        //logger.Debug(ownerRegistrations.Count + " registrations retireved from SIP proxy for " + username + ".");
                        opResult = GetRegistrationIdsToXML(ownerRegistrations);
                    }
                    else
                    {
                        opResult = new XMLServiceResultStruct(false, "No SIP Provider registrations active.", null);
                    }
                }
                else
                {
                    logger.Warn("No username found for authid.");
                    opResult.Message = "Session has timed out, please relogin.";
                }
            }
            catch (Exception excp)
            {
                logger.Error("Exception Get3rdPartyRegistrationIds. " + excp.Message);
                opResult.Message = "Exception processing request. " + excp.Message;
            }

            return opResult.ToStringWithRoot();
        }

		[WebMethod]
        public string GetActiveCalls(string authId)
		{
            XMLServiceResultStruct opResult = new XMLServiceResultStruct(true, UNKNOWN_ERROR_MESSAGE, null);
            
            try
			{
                //logger.Debug("GetActiveCalls request for authid=" + authId + ".");
				
				string username = GetSessionUsername(authId);

				if(username != null)
				{
					//logger.Debug("Username " + username + " found for authid.");

                    //opResult = m_sipProxyServiceProxy.GetActiveCalls(username);

                    //logger.Debug("GetActiveCalls result=" + opResult.ToString());
				}
				else
				{
					logger.Debug("No username found for authid.");
                    opResult.Message = "Session has timed out, please relogin.";
				}
			}
			catch(Exception excp)
			{
                logger.Error("Exception GetActiveCalls. " + excp.Message);
                opResult.Message = "Exception processing request. " + excp.Message;
			}

            return opResult.ToStringWithRoot();
		}

        [WebMethod]
        public string GetLogMessages(string authId, string lastMessageId)
        {
            XMLServiceResultStruct opResult = new XMLServiceResultStruct(true, UNKNOWN_ERROR_MESSAGE, null);

            try
            {
                //logger.Debug("GetLogMessages request for authid=" + authId + ".");

                string username = GetSessionUsername(authId);

                if (username != null)
                {
                    //logger.Debug("Username " + username + " found for authid.");

                    string selectSQL = null;
                    if (lastMessageId == null || lastMessageId.Trim().Length == 0)
                    {
                        selectSQL = "select * from switchlog where username = '" + username + "' order by logid desc limit " + CLIENT_LOGMESSAGE_LIMIT;
                    }
                    else
                    {
                        selectSQL = "select * from switchlog where username = '" + username + "' " +
                            "and logid > " + lastMessageId + " " +
                            "order by logid desc limit " + CLIENT_LOGMESSAGE_LIMIT;
                    }

                    DataSet logSet = m_sipSwitchStorageLayer.GetDataSet(selectSQL);

                    if (logSet != null && logSet.Tables[0].Rows.Count > 0)
                    {
                        string logMessageXML = "<logmessages>";
                        int messageCount = 0;
                        for (int index=logSet.Tables[0].Rows.Count-1; index>=0; index--)
                        {
                            DataRow logRow = logSet.Tables[0].Rows[index];
                            
                            int logMessageId = Convert.ToInt32(logRow["logid"]);
                            string logUsername = logRow["username"] as string;
                            string message = logRow["logmessage"] as string;
                            DateTime inserted = Convert.ToDateTime(logRow["inserted"]);

                            string logMessage =
                                "<logmessage>" +
                                " <logid>" + logMessageId + "</logid>" +
                                //" <message>" + message.Replace('<', '[').Replace('>',']').Replace('&','@') + "</message>" +
                                " <message>" + message.Replace("<", "&lt;") + "</message>" +
                                " <inserted>" + inserted.ToString("HH:mm:ss:fff") + "</inserted>" +
                                "</logmessage>";

                            if (logMessageXML.Length + logMessage.Length > MAX_CLIENTRESPONSE_LENGTH)
                            {
                                break;
                            }
                            else
                            {
                                logMessageXML += logMessage;
                                messageCount++;
                            }
                        }
                        logMessageXML += "</logmessages>";

                        //logger.Debug("log message data=" + logMessageXML + ".");

                        //byte[] logMessageArray = Encoding.ASCII.GetBytes(logMessageXML);
                        //string base64Log = Convert.ToBase64String(logMessageArray, 0, logMessageArray.Length);

                        opResult = new XMLServiceResultStruct(false, messageCount + " retrieved for " + username + ".", logMessageXML);
                        //opResult = new XMLServiceResultStruct(false, logSet.Tables[0].Rows.Count + " retrieved for " + username + ".", base64Log);
                    }
                    else
                    {
                        opResult = new XMLServiceResultStruct(false, "No new messages available.", null);
                    }
                }
                else
                {
                    logger.Debug("No username found for authid.");
                    opResult.Message = "Session has timed out, please relogin.";
                }
            }
            catch (Exception excp)
            {
                logger.Error("Exception GetLogMessages. " + excp.Message);
                opResult.Message = "Exception processing request. " + excp.Message;
            }

            return opResult.ToStringWithRoot();
        }

        /// <summary>
        /// Gets the status and contact lists of the user's third party registrations.
        /// </summary>
        /// <param name="authId"></param>
        /// <returns></returns>
        [WebMethod]
        public string GetRegistrationsFromProxy(string authId)
        {
            XMLServiceResultStruct opResult = new XMLServiceResultStruct(true, UNKNOWN_ERROR_MESSAGE, null);

            try
            {
                //logger.Debug("GetRegistrationsFromProxy request for authid=" + authId + ".");

                string username = GetSessionUsername(authId);

                if (username != null)
                {
                    //logger.Debug("Username " + username + " found for authid.");

                    UserRegistration[] ownerRegistrations = m_regAgentServiceProxy.GetRegistrationAgentRegistrations(username);
                    if (ownerRegistrations != null && ownerRegistrations.Length > 0)
                    {
                        //logger.Debug(ownerRegistrations.Count + " registrations retireved from SIP proxy for " + username + ".");
                        opResult = GetLiveRegistrations(ownerRegistrations);
                    }
                    else
                    {
                        opResult.Message = "No current registrations found.";
                    }
                }
                else
                {
                    logger.Warn("No username found for authid.");
                    opResult.Message = "Session has timed out, please relogin.";
                }
            }
            catch (Exception excp)
            {
                logger.Error("Exception GetUserRegistrations. " + excp.Message);
                opResult.Message = "Exception processing request. " + excp.Message;
            }

            return opResult.ToStringWithRoot();
        }

        /// <summary>
        /// Deletes a user registration and returns a list of all the remaining registrations.
        /// </summary>
        [WebMethod]
        public string DeleteUserRegistration(string authId, string registrationId)
        {
            XMLServiceResultStruct opResult = new XMLServiceResultStruct(true, UNKNOWN_ERROR_MESSAGE, null);

            try
            {
                logger.Debug("DeleteUserRegistration request for authid=" + authId + " and registrationid " + registrationId + ".");

                string username = GetSessionUsername(authId);

                if (username != null)
                {
                    logger.Debug("Username " + username + " found for authid.");

                    Guid regId = new Guid(registrationId);

                    string opMessage = null;
                    bool opError = false;

                    try
                    {
                        string deleteSQL = "delete from registrations where registrationid = '" + regId + "' and username = '" + username + "'";

                        m_sipSwitchStorageLayer.ExecuteNonQuery(deleteSQL);

                        // Delete registration from the proxy.
                        //m_regAgentServiceProxy.DeleteSIPRegistration(regId);
                        //m_regAgentProvisioningWebService

                        opMessage = "Registration successfully deleted.";
                    }
                    catch (Exception delExcp)
                    {
                        logger.Error("Exception DeleteUserRegistration. " + delExcp.Message);
                        opMessage = delExcp.Message;
                        opError = true;
                    }

                    opResult = GetUserRegistrationsInternal(username);
                    opResult.Error = opError;
                    opResult.Message = opMessage + " " + opResult.Message;
                }
                else
                {
                    logger.Debug("No username found for authid.");
                    opResult.Message = "Session has timed out, please relogin.";
                }
            }
            catch (Exception excp)
            {
                logger.Error("Exception DeleteUserRegistration. " + excp.Message);
                opResult.Message = "Exception processing request. " + excp.Message;
            }

            return opResult.ToStringWithRoot();
        }
       
        /// <summary>
        /// Gets a list of the a users's registrations with the SIP Switch.
        /// </summary>
        [WebMethod]
        public string GetUserRegistration(string authId)
        {
            XMLServiceResultStruct opResult = new XMLServiceResultStruct(true, UNKNOWN_ERROR_MESSAGE, null);

            try
            {
                //logger.Debug("GetUserRegistration request for authid=" + authId + ".");

                string username = GetSessionUsername(authId);

                if (username != null)
                {
                    //logger.Debug("Username " + username + " found for authid.");

                    SIPParameterlessURI addressOfRecord = new SIPParameterlessURI(SIPSchemesEnum.sip, m_sipSwitchRealm, username);
                    //SIPRegistrarRecord switchRegistration = m_proxyInfoServiceProxy.GetSwitchRegistration(addressOfRecord);

                    string selectSQL = "select * from sipaccountcontacts sac join sipaccounts sip on sac.sipaccountid = sip.sipaccountid where owner = '" + username.Replace("'", "''") + "'";
                    DataSet sipSet = m_sipSwitchStorageLayer.GetDataSet(selectSQL);

                    //if (switchRegistration != null && switchRegistration.GetBindings() != null)
                    if(sipSet != null && sipSet.Tables[0].Rows.Count > 0)
                    {
                        string userRegXML = "<userregistrations>";

                        //foreach (SIPAddressBinding binding in switchRegistration.GetBindings())
                        foreach(DataRow sipRow in sipSet.Tables[0].Rows)
                        {
                            //SIPAccount sipAccount = new SIPAccount(sipRow);
                            //logger.Debug("Useragent for " + addressOfRecord.ToString() + " is " + binding.UserAgent + ".");
                            //logger.Debug("SDPOwner for " + addressOfRecord.ToString() + " is " + binding.SDPOwner + ".");

                            //SIPUserAgentsEnum userAgent = SIPUserAgents.GetUserAgent(binding.UserAgent);

                            string contactId = sipRow["contactid"] as string;
                            string contactURI = sipRow["contacturi"] as string;
                            string userAgentStr = sipRow["useragent"] as string;
                            DateTime expiresAt = (DateTime)sipRow["expiresat"];
                            DateTime lastUpdate = (DateTime)sipRow["lastupdate"];

                            SIPUserAgentsEnum userAgent = SIPUserAgents.GetUserAgent(userAgentStr);
                            double expires = expiresAt.Subtract(DateTime.Now).TotalSeconds;

                            userRegXML +=
                                 "<userregistration id='" + contactId + "'>" +
                                 "<contact>" + SafeXML.MakeSafeXML(contactURI) + "</contact>" +
                                 "<useragent>" + userAgent + "</useragent>" +
                                 "<expires>" + expires.ToString("0") + "</expires>" +
                                 "<lastregistration>" + lastUpdate.ToString("dd MMM yyyy HH:mm:ss") + "</lastregistration>" +
                                 "</userregistration>";
                        }

                        userRegXML += "</userregistrations>";

                        opResult = new XMLServiceResultStruct(false, "Registration found.", userRegXML);
                    }
                    else
                    {
                        string userRegXML = "<userregistrations/>";
                        opResult = new XMLServiceResultStruct(false, "User " + addressOfRecord.ToString() + " not currently logged in to SIPSwitch Proxy server.", userRegXML);
                    }
                }
                else
                {
                    logger.Debug("No username found for authid.");
                    opResult.Message = "Session has timed out, please relogin.";
                }
            }
            catch (Exception excp)
            {
                logger.Error("Exception GetUserRegistration. " + excp.Message);
                opResult.Message = "Exception processing request. " + excp.Message;
            }

            return opResult.ToStringWithRoot();
        }

        /// <summary>
        /// Attempts to remove a registration contact.
        /// </summary>
        [WebMethod]
        public string RemoveRegistrarContact(string authId, string registrationId, string contactToRemove)
        {
            XMLServiceResultStruct opResult = new XMLServiceResultStruct(true, UNKNOWN_ERROR_MESSAGE, null);

            try
            {
                logger.Debug("RemoveRegistrarContact request for authid=" + authId + " and registrationid " + registrationId + ".");

                string username = GetSessionUsername(authId);

                if (username != null)
                {
                    logger.Debug("Username " + username + " found for authid.");
                    //m_regAgentServiceProxy.RemoveSIPContact(new Guid(registrationId), contactToRemove);

                    // Give the request a chance to be processed.
                    Thread.Sleep(1000);

                    string updatedReg = Get3rdPartyRegistrationDetails(authId, registrationId);
                     return updatedReg;
                }
                else
                {
                    logger.Debug("No username found for authid.");
                    opResult.Message = "Session has timed out, please relogin.";
                }
            }
            catch (Exception excp)
            {
                logger.Error("Exception RemoveRegistrarContact. " + excp.Message);
                opResult.Message = "Exception processing request. " + excp.Message;
            }

            return opResult.ToStringWithRoot();
        }

        /// <summary>
        /// Attmepts to get an updated contact list for a particular 3rd party registration.
        /// </summary>
        [WebMethod]
        public string RefreshRegistrationView(string authId, string registrationId)
        {
            XMLServiceResultStruct opResult = new XMLServiceResultStruct(true, UNKNOWN_ERROR_MESSAGE, null);

            try
            {
                logger.Debug("RefreshRegistrationView request for authid=" + authId + " and registrationid " + registrationId + ".");

                string username = GetSessionUsername(authId);

                if (username != null)
                {
                    logger.Debug("Username " + username + " found for authid.");
                    //m_regAgentServiceProxy.RefreshRegistrationView(new Guid(registrationId));

                    // Give the request a chance to be processed.
                    Thread.Sleep(2000);

                    string updatedReg = Get3rdPartyRegistrationDetails(authId, registrationId);
                    return updatedReg;
                }
                else
                {
                    logger.Debug("No username found for authid.");
                    opResult.Message = "Session has timed out, please relogin.";
                }
            }
            catch (Exception excp)
            {
                logger.Error("Exception RefreshRegistrationView. " + excp.Message);
                opResult.Message = "Exception processing request. " + excp.Message;
            }

            return opResult.ToStringWithRoot();
        }

        /// <summary>
        /// Sends an MWI request to the specified contact URI.
        /// </summary>
        [WebMethod]
        public string SendMWIRequest(string authId, string uaContacrURI, bool setMWI)
        {
            XMLServiceResultStruct opResult = new XMLServiceResultStruct(true, UNKNOWN_ERROR_MESSAGE, null);

            try
            {
                logger.Debug("SendMWIRequest request for authid=" + authId + " and contact " + uaContacrURI + ".");

                string username = GetSessionUsername(authId);

                if (username != null)
                {
                    logger.Debug("Username " + username + " found for authid.");

                    //m_sipProxyServiceProxy.SendMWIRequest(uaContacrURI, setMWI, username);

                    opResult.Message = "MWI request successfully submitted for " + SafeXML.MakeSafeXML(uaContacrURI) + ".";
                    opResult.Error = false;
                }
                else
                {
                    logger.Debug("No username found for authid.");
                    opResult.Message = "Session has timed out, please relogin.";
                }
            }
            catch (Exception excp)
            {
                logger.Error("Exception SendMWIRequest. " + excp.Message);
                opResult.Message = "Exception processing request. " + excp.Message;
            }

            return opResult.ToStringWithRoot();
        }

        /// <summary>
        /// Sends an Invite request to the specified contact URI.
        /// </summary>
        [WebMethod]
        public string SendInviteRequest(string authId, string uaContacrURI, string callerIdName, string callerIdUser)
        {
            XMLServiceResultStruct opResult = new XMLServiceResultStruct(true, UNKNOWN_ERROR_MESSAGE, null);

            try
            {
                logger.Debug("SendInviteRequest request for authid=" + authId + " and contact " + uaContacrURI + " and " + callerIdName + " " + callerIdUser + ".");

                string username = GetSessionUsername(authId);

                if (username != null)
                {
                    logger.Debug("Username " + username + " found for authid.");

                    //m_sipProxyServiceProxy.SendInviteRequest(uaContacrURI, callerIdName, callerIdUser, username);

                    opResult.Message = "Invite request successfully submitted for " + SafeXML.MakeSafeXML(uaContacrURI) + ".";
                    opResult.Error = false;
                }
                else
                {
                    logger.Debug("No username found for authid.");
                    opResult.Message = "Session has timed out, please relogin.";
                }
            }
            catch (Exception excp)
            {
                logger.Error("Exception SendInviteRequest. " + excp.Message);
                opResult.Message = "Exception processing request. " + excp.Message;
            }

            return opResult.ToStringWithRoot();
        }

		#endregion

		private string GetSessionUsername(string authId)
		{
			try
			{
                string selectSQL = "select username, lastcontacttime, now() as currenttime from AuthenticatedSessions where SessionId = '" + authId.Replace("'", "''") + "' and Expired <> '1'";

				DataSet lastTimeDS = m_sipSwitchStorageLayer.GetDataSet(selectSQL);

				DateTime sessionTimestamp = DateTime.MinValue;
				DateTime currentTimestamp = DateTime.MinValue;
				string username = null;

				if(lastTimeDS != null && lastTimeDS.Tables[0].Rows.Count > 0)
				{
					//logger.Debug("Data row found for authid " + authId + ".");
					
					DataRow sessionRow = lastTimeDS.Tables[0].Rows[0];

					sessionTimestamp = (DateTime)sessionRow["lastcontacttime"];
					currentTimestamp = (DateTime)sessionRow["currenttime"];
					username = sessionRow["username"] as string;

					//logger.Debug("Last contact time for " + username + " was " + sessionTimestamp.ToString("dd MMM yyyy HH:mm:ss") + ", current time is " + currentTimestamp.ToString("dd MMM yyyy HH:mm:ss") + ".");
				}

				if(currentTimestamp - sessionTimestamp < m_sessionDuration)
				{
                    m_sipSwitchStorageLayer.ExecuteNonQuery("update AuthenticatedSessions set LastContactTime = now() where SessionId = '" + authId.Replace("'", "''") + "'");
					
					return username;
				}
				else
				{
                    m_sipSwitchStorageLayer.ExecuteNonQuery("update AuthenticatedSessions set Expired = '1' where SessionId = '" + authId.Replace("'", "''") + "'");

					return null;
				}
			}
			catch(Exception excp)
			{
				logger.Error("Exception GetSessionUsername. " + excp.Message);
				return null;
			}
		}

        private XMLServiceResultStruct GetUserRegistrationsInternal(string username)
        {
            throw new ApplicationException("ToDo switch to SIP Proivder approach.");
            /*string selectSQL = "select * from registrations where username = '" + username.Replace("'", "''") + "'";

            DataSet regSet = m_sipSwitchStorageLayer.GetDataSet(selectSQL);

            if (regSet != null && regSet.Tables[0].Rows.Count > 0)
            {
                string regXML = "<registrations>";
                foreach (DataRow regRow in regSet.Tables[0].Rows)
                {
                    UserRegistration userReg = new UserRegistration(regRow);
                    regXML += userReg.ToXML();
                }
                regXML += "</registrations>";

                int regCount = regSet.Tables[0].Rows.Count;
                string regMessage = null;
                if (regCount == 1)
                {
                    regMessage = "One registration found.";
                }
                else
                {
                    regMessage = regCount + " registrations were found.";
                }

                return new XMLServiceResultStruct(false, regMessage, regXML);
            }
            else
            {
                return new XMLServiceResultStruct(false, "No registrations were found.", null);
            }*/
        }

        private XMLServiceResultStruct GetLiveRegistrations(UserRegistration[] ownerRegistrations)
        {
            if (ownerRegistrations != null && ownerRegistrations.Length > 0)
            {
                string regXML = "<registrations>";
                foreach (UserRegistration userReg in ownerRegistrations)
                {
                    //logger.Debug("Putting registration for " + userReg.Registrar + " to XML.");

                    //try
                    //{
                        regXML += userReg.ToXML();
                    //}
                    //catch (Exception excp)
                    //{
                    //    logger.Error("Exception GetLiveRegistrations XML. " + excp.Message);
                    //}
                }
                regXML += "</registrations>";

                int regCount = ownerRegistrations.Length;
                string regMessage = null;
                if (regCount == 1)
                {
                    regMessage = "One registration found.";
                }
                else
                {
                    regMessage = regCount + " registrations were found.";
                }

                //logger.Debug("Registrations XML=" + regXML + ".");

                return new XMLServiceResultStruct(false, regMessage, regXML);
            }
            else
            {
                return new XMLServiceResultStruct(false, "No registrations found.", null);
            }
        }

        private XMLServiceResultStruct GetRegistrationIdsToXML(UserRegistration[] ownerRegistrations)
        {
            if (ownerRegistrations != null && ownerRegistrations.Length > 0)
            {
                string regXML = "<registrations><registrationids>";
                foreach (UserRegistration userReg in ownerRegistrations)
                {
                    regXML += "<id>" + userReg.RegistrationId + "</id>";
                }
                regXML += "</registrationids></registrations>";

                int regCount = ownerRegistrations.Length;
                string regMessage = null;
                if (regCount == 1)
                {
                    regMessage = "One registration found.";
                }
                else
                {
                    regMessage = regCount + " registrations were found.";
                }

                return new XMLServiceResultStruct(false, regMessage, regXML);
            }
            else
            {
                return new XMLServiceResultStruct(false, "No registrations found.", null);
            }
        }
	}
}
